/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#ifndef __VECTOR3_H__
#define __VECTOR3_H__

#include <math.h>
#include <Converter.h>
#include <algorithm>
#include <limits> 

namespace liba
{
namespace math
{

template<typename T>
class Vector3
{
public:
	T x, y, z;

	Vector3()
	{
		x = y = z = 0;
	}

	Vector3(T x, T y, T z)
	:	x(x),
		y(y),
		z(z)
	{}

	Vector3( const Vector3<T>  & vec )
	{
		x = vec.x;
		y = vec.y;
		z = vec.z;
	}

	template< typename U >
	Vector3( const Vector3<U>  & vec )
	{
		x = vec.x;
		y = vec.y;
		z = vec.z;
	}

	const T * data()const
	{
		return &x;
	}

	T * data()
	{
		return &x;
	}

	const T & data( size_t i )const
	{
		return (&x)[i];
	}

	T & data( size_t i )
	{
		return (&x)[i];
	}

	T & operator[] ( size_t i )
	{
		return data(i);
	}

	const T & operator[] ( size_t i )const
	{
		return data(i);
	}

	Vector3 operator- ()const
	{
		return Vector3(-x, -y, -z);
	}

	const Vector3 & operator+= ( const Vector3 & vec )
	{
		x += vec.x;
		y += vec.y;
		z += vec.z;
		return *this;
	}

	const Vector3 & operator-= ( const Vector3 & vec )
	{
		x -= vec.x;
		y -= vec.y;
		z -= vec.z;
		return *this;
	}

	const Vector3 & operator*= ( const T & f )
	{
		x *= f;
		y *= f;
		z *= f;
		return *this;
	}

	const Vector3 & operator/= ( const T & f )
	{
		x /= f;
		y /= f;
		z /= f;
		return *this;
	}

	static T dot( const Vector3 & u, const Vector3 & v )
	{
		return u.x*v.x + u.y*v.y + u.z*v.z;
	}

	T length_2()const
	{
		return dot( *this, *this );
	}

	T length_2( const Vector3 & vec )const
	{
		Vector3 temp_vec = vec - *this;
		return dot( temp_vec, temp_vec );
	}

	T length()const
	{
		return T( sqrt( length_2() ) );
	}

	T length( const Vector3 & vec )const
	{
		return T( sqrt( length_2(vec) ) );
	}

	void normalize()
	{
		T l = length();
		if( l != T(0) ) 
		{
			*this /= l;
		}
	}

	static Vector3 normalize(const Vector3 & v)
	{
		Vector3 n(v);
		n.normalize();
		return n;
	}

	static Vector3 zero()
	{
		return Vector3(0, 0, 0);
	}

	static Vector3 cross( const Vector3 & u, const Vector3 & v )
	{
		return Vector3( u.y*v.z - u.z*v.y,
			u.z*v.x - u.x*v.z,
			u.x*v.y - u.y*v.x );
	}

	static T angle( const Vector3 & u, const Vector3 & v )
	{
		return T( acos( dot(u, v) ) );
	}

	bool go_to( const Vector3 & future, const T & sp )
	{
		Vector3 delta = future - *this;
		T len = delta.length();
		if( len <= sp )
		{
			*this = future;
			return true;
		}
		delta.normalize();
		*this += delta * sp;
		return false;
	}

	template<typename Sym>
	struct ConverterBuffer
		:	public converter::ConverterStaticBuffer<Sym, 28*3>
	{
		typedef converter::ConverterStaticBuffer<Sym, 28*3> StaticBuffer;
		bool convert(const Vector3 & value)
		{
			StaticBuffer::size = converter::simple_print( StaticBuffer::data, value[0] );
			for( int i = 1; i < 3; ++i )
			{
				StaticBuffer::data[StaticBuffer::size++] = ' ';
				StaticBuffer::size += converter::simple_print( StaticBuffer::data + StaticBuffer::size, value[i] );
			}
			return true;
		}
	};

	template<class Sym>
		bool convert(const Sym * be, const Sym * en)
	{
		const Sym * beg = be;
		for( int i = 0; i < 3; ++i )
		{
			beg = converter::simple_convert( beg, en, &operator[](i));
			if( !beg )
				return false;
		}
		return true;
	}
};

template<typename T>
inline Vector3<T> operator + (const Vector3<T> & u,const Vector3<T> & v )
{
	return Vector3<T>( u ) += v;
}

template<typename T>
inline Vector3<T> operator - (const Vector3<T> & u,const Vector3<T> & v )
{
	return Vector3<T>( u ) -= v;
}

template<typename T>
inline Vector3<T> operator * (const Vector3<T> & u, const T & f )
{
	return Vector3<T>( u ) *= f;
}

template<typename T>
inline Vector3<T> operator * (const T & f, const Vector3<T> & u )
{
	return Vector3<T>( u ) *= f;
}

template<typename T>
inline Vector3<T> operator / ( const Vector3<T> & u, const T & f )
{
	return Vector3<T>( u ) /= f;
}

inline Vector3<float> operator * (const Vector3<float> & u, const double & f )
{
	return Vector3<float>( u ) *= float(f);
}

inline Vector3<float> operator / ( const Vector3<float> & u, const double & f )
{
	return Vector3<float>( u ) /= (float)f;
}

template<typename T>
inline T norma( const Vector3<T> & a, const Vector3<T> & b )
{
	return a.length( b );
}

template<class T>
inline bool operator== ( const Vector3<T>& lhs, const Vector3<T> & rhs )
{
	return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
}

template <> inline bool operator==(const Vector3<float>& lhs, const Vector3<float>& rhs)
{
	const float epsilon = std::numeric_limits<float>::epsilon();

	return 
		fabs(lhs.x - rhs.x) < epsilon && 
		fabs(lhs.y - rhs.y) < epsilon &&
		fabs(lhs.z - rhs.z) < epsilon;
}

template <> inline bool operator==(const Vector3<double>& lhs, const Vector3<double>& rhs)
{
	const double epsilon = std::numeric_limits<double>::epsilon();

	return 
		fabs(lhs.x - rhs.x) < epsilon && 
		fabs(lhs.y - rhs.y) < epsilon &&
		fabs(lhs.z - rhs.z) < epsilon;
}

template<class T>
inline bool operator!= (const Vector3<T>& lhs, const Vector3<T>& rhs)
{
	return ! (lhs == rhs);
}

} // namespace math
} // namespace liba

using namespace liba;

#endif //__VECTOR3_H__
